	package com.darkflame.client.semantic;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.logging.Logger;

import com.darkflame.client.SuperSimpleSemantics;
import com.darkflame.client.Utilitys;
import com.darkflame.client.interfaces.GenericWaitForRepeating.MyRepeatingCommand;
import com.darkflame.client.interfaces.SSSGenericFileManager.FileCallbackError;
import com.darkflame.client.interfaces.SSSGenericFileManager.FileCallbackRunnable;
import com.darkflame.client.semantic.SimpleLoadingQueue.LoadRequest;


/** The super simple semantic node class
 * This represents a single unique entity in a semantic database
 * ie. "green" or "fruit" **/

public class SSSNode  {
	
	/** if true, the parent and child caches should be updated both retrieving values **/
	boolean NeedsUpdate = true;

	static Logger Log = Logger.getLogger("sss.SSSNode");
	
	//primary label
	String PLabel = "";
	
	static boolean extendedDebug = false;
	
	
	
	static public void setExtendedDebug(boolean state){
		extendedDebug=state;
	}

	public String getAllPLabels() {
		String labs=PLabel;
		for (String lab : altLabs) {
			
			labs=labs+" , "+lab;
			
		}
		
		return labs;
	}
	
	/** get the primary label for this node **/
	public String getPLabel() {
		return PLabel;
	}

	/** get the primary URI for this node **/
	public String getPURI() {
		return PURI;
	}
	/** get the primary uri in short form if a prefix for this namespace is known
	 *  in future this should be worked out in advance as it never changes**/
	public String getShortPURI() {
		

		if (!PURI.contains("#")){
			return PURI;
		}
		//get namespace before #
		
		//String namespace = PURI.substring(0,PURI.indexOf("#"));		
	//	  String justname  = PURI.substring(PURI.indexOf("#"));
		
		
	//	Log.info("namespace="+namespace);
	//	Log.info("justname="+justname);
		
		//check if prefix exists
		String prefix = RawQueryUtilities.getPrefix(PURI);
		
		Log.info("prefix="+prefix);
		
		return prefix;
	}
	//alternative labels known
	HashSet<String> altLabs = new HashSet<String>();
	
	//primary uri
	public String PURI = "";
	
	
	//Equivalent nodes
	HashSet<String> altURIs = new HashSet<String>();
	
	
	//nodes that are a subclass of this
	ArrayList<SSSNode> knownSubclassesOfThis = new ArrayList<SSSNode>();
	
	//nodes that this one is a subclass of
	//This is a convenient mirroring of what's stored in the CommonProperty lists, to speed to searches
	//at the expense of Ram. This could be removed only if the "isorhasparentclass" is changed to use the commonpropertysets.
	ArrayList<SSSNode> knownParentClasses= new ArrayList<SSSNode>();
	
	//Static list of all known nodes
	
	static ArrayList<SSSNode> knownNodes= new ArrayList<SSSNode>();
	
	//static classes
	//main rdsf things we support
	public static SSSNode SubClassOf = new SSSNode("ClassOf","rdfs:subClassOf");
	public static SSSNode Label = new SSSNode("Label","rdfs:label");
	
	//owl we will support
	public static SSSNode Equivlient = new SSSNode("EquivalentTo", "owl:equivalentClass");
	
	
	//SSS internal query predicates
	//Note; proposed support only at this stage, the idea is when searching you can use
	// "SSS:StartsWith" to check is a objects label starts with a specified string
	public static SSSNode STARTSWITH = new SSSNode("StartsWith","sss:StartsWith");
	public static SSSNode ENDSSWITH = new SSSNode("EndsWith","sss:EndssWith");
	
	
	
	
	//ERROR NODES, internal use only
	public static SSSNode ERROR = new SSSNode("Error","GFE:Error"); //should be a subclass of error
	public static SSSNode NOTFOUND = new SSSNode("No Matching Nodes Found","Error:NoMatchingNodesFound",new SSSNode[] {ERROR});
	
	/** The queue of nodes to be merged to other nodes when a OWL:EquiliventTo is called**/
	static LinkedList<MergeRequest> pendingMergeRequests = new LinkedList<MergeRequest>(); //First in, first out array
	static boolean MergeInProcess=false;
	
	static public class MergeRequest
	{



		private SSSNode thisNode;
		private String mergeSupportedBy;
		
		private SSSNode intoThisNode;

		public MergeRequest(SSSNode thisNode, SSSNode intoThisNode,
				String mergeSupportedBy) {
			
			this.thisNode=thisNode;
			this.intoThisNode=intoThisNode;
			this.mergeSupportedBy =mergeSupportedBy;
			
			
			
		}

		
	}
	
	//Note; Need to move quote mark support to here
	//In order to remove aibigiuity between stuff in quotes with colons
	// eg " blah : blah "
	// without the quotes the program cant tell if that blah: is a prefix or part of a word
	
	//therefor we will always look for the first quote and treate that as the end of any searches 
	//for colons, as searching for slashs backwards from that point
	//the pURI remains unchanged, this is all just for correct label isolation
	
	// "<:jghj>"
	// fmm:"blah:blah"
	//www.fanficmaker.com/test/test.ntlist#"blah:blah"
	//
	// Label is now; Whatever is after the first : or the first # (whatever is sooner)
  	
	/** safely create a new node from just a uri, or return it if it exists already 
	 * the label will be assigned based on the uri itself, cropping of the prefix if needed
	 * @param Primary Uri
	 * @param Primary default namespace (if not part of uri)
	 *  **/
	public static SSSNode createSSSNode(String pURI, String defaultNS) {
		
		//temp randomisation of capitalization
		//This is a CRAZY thing to DO...But we are doing it to ensure the robustness of the case insensative accross the whole SSS system
		//If its 2015 or more and this is still in...remove it
		
		//int len = pURI.length()-1;
		//int pr = (int) (Math.random()*len);
		//pURI = pURI.substring(0, pr).toUpperCase() + pURI.substring(pr);
		//----------------
		//--------------------
		
				
		String pLabel = extractLabelFromURI(pURI);
		
		return createSSSNode(pLabel, pURI, defaultNS,null); 
		
	}

	/**
	 * Attempts to extract a label from the given full uri
	 * A label will, ideally, crop of all the uri/location information and return just the bit after the first #
	 * <br>
	 * ie. www.darkflame.co.uk/semantic.ntlist#green
	 * returns green
	 * 
	 * Note; Different uris can have the same label. Labels are not unique. URIs should always be used for comparison, Labels just for display
	 * 
	 * @param pURI
	 * @return a label
	 */
	public static String extractLabelFromURI(String pURI) {
		String pLabel = pURI;
		
		//detect any opening quote marks
		//int OpeningQuoteLocation = pURI.indexOf("\"");
		
		//detect any colons :
		//int FirstColonsLocation = pURI.indexOf(":");
		
		//detect any #
		int earliestPos = pURI.indexOf("#");
		
		
		
		//Ok, from the above the label will be whatever starts at the earliest non-zero value
		//int earliestPos = RawQueryUtilities.smallestNonNegative(OpeningQuoteLocation,FirstColonsLocation,FirstHashLocation);
		
		//Log.info("extracting label:"+OpeningQuoteLocation+","+FirstColonsLocation+","+FirstHashLocation);
		
		
		/*
		if (pURI.contains(":")){
								
			pLabel = pURI.split(":")[1];
			
			if (pURI.contains("/")){
				int lastslash = pLabel.lastIndexOf("/")+1;
				pLabel = pLabel.substring(lastslash);
				
				//labels should always be lower case for consistency
				pLabel = pLabel.toLowerCase();
				
			}
			

			
		} */
		
		if (earliestPos>0){
			
			pLabel = pURI.substring(earliestPos+1);
			
			Log.info("label is now : "+pLabel);
			
			
			
		}
		return pLabel;
	}
	/** safely create a new node, or return it if it exists already **/
	public static SSSNode createSSSNode(String pLabel, String pURI, String defaultNS) {
		return createSSSNode(pLabel, pURI, defaultNS,null); 
	}
	
	
	/** safely create a new node, or return it if it exists already <br>
	 * <br>
	 * If theres a colon outside a quote and before a #, then its assumed<br>
	 * to be a prefix and will be expanded.<br>
	 * Below is examples of some supplied uris and what will be stored internally for them<br>;<br>
	 * (assuming blh prefix is associated with blah.ntlist)<br>
	 * <br>
	 * eg<br>
	 * blh:"test:test"                 >>>          blah.ntlist#"test:test"     <br>
	 * blah.ntlist#"test:test"         >>>          blah.ntlist#"test:test"     <br>
	 * blah.ntlist#"blah.ntlist#Test"  >>>          blah.ntlist#"blah.ntlist#Test"  <br>
	 * "blh:test.ntlist"                   >>> defaultdomain.ntlist#"blh:test.ntlist"<br>
	 * **/
	public static SSSNode createSSSNode(String pLabel, String pURI, String defaultNS, SSSNode[] parents) {
		
		pURI=pURI.trim();
		pLabel=pLabel.trim();
		
		pLabel = RawQueryUtilities.stripQuotes(pLabel);
				
		//expand prefix if needed
		if (pURI.contains(":")){
			Log.info("potential prefix in uri detected");
			pURI = RawQueryUtilities.getURI(pURI);
			Log.info("is now:"+pURI);
		}
//
		//predicate = RawQueryUtilities.stripQuotes(pURI);
		
		
		boolean isFileName  = Utilitys.isFilename(pURI);
		
		Log.info(pURI+" is filename:"+isFileName);
		
		//add the defaultNS if needed:
		
		//we used to use this to determine filename rather then the utility method
		//if (!pURI.contains("#")&&(!pURI.contains("http://")))
		
		boolean alreadyHasHash = false;
		int hashloc = pURI.indexOf("#");
		int quoteloc = pURI.indexOf("\"");
		
		//if theres a hash before a quote
		if ( (hashloc!=-1) && (hashloc<quoteloc || quoteloc==-1) ){
			alreadyHasHash = true;
		}		
		
				
		
		if ((!alreadyHasHash &&(!pURI.startsWith("http://")))&&(!isFileName))
		{
			//only after doing the above and we have a safe pURI to use do we strip the quotes from the uri
			pURI = RawQueryUtilities.stripQuotes(pURI);
			
			Log.info("adding default ns:"+defaultNS+" to node");
			
			pURI=defaultNS+pURI;
			
		} else {
			//only after doing the above and we have a safe pURI to use do we strip the quotes from the uri
			pURI = RawQueryUtilities.stripQuotes(pURI);
			
		}
		

		
		SSSNode newnode = SSSNode.getNodeByUri(pURI);
		
		if (newnode==null||(newnode==SSSNode.NOTFOUND)){
			
			Log.info("from create");
			if (parents!=null){
				newnode =  new SSSNode(pLabel, pURI,parents);
			} else {
				newnode = new SSSNode(pLabel, pURI);
			}
			
		} else {
			
			Log.warning("################## Existing Node Found:"+newnode.getPURI());
			
		}
		
		return newnode;
		
	}
	
	@Override
	public int hashCode(){
		//based on the primary uri
		return PURI.hashCode();
		
	}
	
	@Override
	public boolean equals(Object c){
		
		if (c.getClass()==super.getClass()){
			
			return  ((SSSNode)c).PURI.equals(this.PURI);
			
		}
		
		return false;
	}
	
	//ignore my crap spelling, this is somewhat rushed.
	/** Adds a class which is equilievent to this.
	 * This is private because they should always be declared as equilent to eachother,
	 * which is done with SSSNode.declareAsEquilivent(SSSNode this,SSSNode isTheSameAsThis **/
	private void addEquivalent(String equivalent){
		altURIs.add(equivalent);
	}
	

	static public void mergeNode(SSSNode thisNode,SSSNode intoThisNode,String mergeSupportedBy)
	{
		
		//to help stop concurrent modification errors we only merge one node at a time	
		
		//add to the merge queue
		pendingMergeRequests.add(new MergeRequest(thisNode, intoThisNode, mergeSupportedBy));
		
		testMergeQueue();
		
		//test queue
		//mergeNodeSafely(thisNode,intoThisNode,mergeSupportedBy);
		
	}
	
	static private void testMergeQueue(){
		
		if (pendingMergeRequests.size()>0 && !MergeInProcess){
			
			mergeNodeSafely(pendingMergeRequests.removeFirst());
			
		} else {
			
			Log.info("merge in progress,waiting to start next merge");
			
		}
		
	}
	
	static private void mergeNodeSafely(MergeRequest mergedetails)
	{
		SSSNode thisNode =mergedetails.thisNode;
		SSSNode intoThisNode=mergedetails.intoThisNode;
		String mergeSupportedBy = mergedetails.mergeSupportedBy;

		
		SuperSimpleSemantics.info("merging "+thisNode.PURI+" into "+intoThisNode.PURI);

		if (thisNode==intoThisNode){
			Log.info("nodes are the same! cant merge into itself!");
			MergeInProcess=false;
			testMergeQueue();
			return;
		}
		
		MergeInProcess=true;
		//add to "intoThisNode" equivalent list
		
		//add the source nodes primary uri
		intoThisNode.addEquivalent(thisNode.PURI);
		
		//add its secondary too
		for (String altURI : thisNode.altURIs) {			
			intoThisNode.addEquivalent(altURI);			
		}
		
		//add primary label
		intoThisNode.addLabel(thisNode.PLabel);
	//	Log.info("copying plabel:"+thisNode.PLabel+" to "+intoThisNode.PURI);
	//	Log.info("node has:"+intoThisNode.getAllPLabels());
	//	Log.info("other node has:"+thisNode.getAllPLabels());
		
		
		//copy over all the alt labels too
		for (String altLabel : thisNode.altLabs) {	
			Log.info("copying label:"+altLabel+" to "+intoThisNode.PURI);
			intoThisNode.addLabel(altLabel);		
		}
		
		Log.warning("node now has:"+intoThisNode.getAllPLabels());
		
		//now copy its paturn and children
		intoThisNode.knownParentClasses = thisNode.knownParentClasses;
				
		for (SSSNode parent : thisNode.knownParentClasses) {
			
			intoThisNode.addParentClasses(parent,mergeSupportedBy);		
			
		}
		
		intoThisNode.knownSubclassesOfThis = thisNode.knownSubclassesOfThis;	
		
		for (SSSNode child : thisNode.knownSubclassesOfThis) {
			
			intoThisNode.addChildClasses(child);
			
		
		}
		
		//update all common property lists to use the new node rather then the old one
		SSSNodesWithCommonProperty.replaceNodeInSetDefinitions(thisNode,intoThisNode);
		SSSNodesWithCommonProperty.replaceNodeInSets(thisNode,intoThisNode,mergeSupportedBy);
		
		SSSIndex.replaceNodesInIndexs(thisNode,intoThisNode);
		
		//Finally we have to remove all reference to the old one, so all querys will
		//be directed towards the new one one
		knownNodes.remove(thisNode);
		
		thisNode.PURI = "Error This Node Not Used Due To Merge";
		thisNode.PLabel =  "Error This Node Not Used Due To Merge";

		SSSNodesWithCommonProperty.logAllSets();
		
		Log.warning("-----------------------------------------------------------------merge finnished");
		MergeInProcess=false;
		
		//test for any other merges needed
		testMergeQueue();
		
		//temp test
	   // final SSSNode writer = SSSNode.getNodeByUri("http://dbpedia.org/ontology/Writer");
	   // Log.warning("writer test:"+writer.getPURI()+" ( "+writer.getEquivilentsAsString());
	    
	    
	}
	
	
	public String getEquivilentsAsString(){
		
		String str="";
		
		Iterator<String> eit = altURIs.iterator();
		
		while (eit.hasNext()) {
			//SSSNode sssNode =(SSSNode) eit.next();
			String uri =  eit.next();
			
			str=str+","+uri;
			
			//str=str+","+sssNode.PURI;
			
		}
		
		return str;
	}
	
	/**
	 * tests if a primary label has been explictly set yet, rather then implictly extracted from the uri string
	 * @return
	 */
	public boolean hasLabel(){
		
		//if its using the PURI as a label, then no label is set.
		//we test if the PURI in various forms equals the PLabel
		//as this would indicate its been set automatically in some way
		if (PURI.equalsIgnoreCase(PLabel) || getShortPURI().equalsIgnoreCase(PLabel) || SSSNode.extractLabelFromURI(PURI).equalsIgnoreCase(PLabel)){
			
			
			
			return false;
			
		} else {
			
			return true;
			
		}
		
	}
	
	private SSSNode(String pLabel, String pURI) {
		
		super();
		PLabel = pLabel.trim();
		PURI = pURI.trim();
		
		Log.warning("******************1111New Node "+pURI+"("+PLabel+") Created TotalNodes = "+knownNodes.size());
		
		knownNodes.add(this);
		
	}
	private SSSNode(String pLabel, String pURI,SSSNode[] parents) {
		super();
		PLabel = pLabel.trim();
		PURI = pURI.trim();
		knownParentClasses = new ArrayList<SSSNode>(Arrays.asList(parents));
		//add parent classes to 
		addParentClassesToSets(knownParentClasses,"Internal");
		
		Log.warning("******************~~~~New Node "+pURI+"("+PLabel+") Created TotalNodes = "+knownNodes.size());
		
		
		knownNodes.add(this);
	}
	
	// adds all the parent classes to set - the sets are what "really" stores the parent/child relationships
	private void addParentClassesToSets(ArrayList<SSSNode> knownParentClasses, String accordingToSource) {
		

		Iterator<SSSNode>  parents = knownParentClasses.iterator();
		
		while (parents.hasNext()) {
			
			SSSNode parent = parents.next();		
			SSSNodesWithCommonProperty.addParentRelationship(this,parent,accordingToSource);
				
		}
		
		
	}
	
	/**
	 * use SSSNode.createSSSNode instead
	 * @param pLabel
	 * @param pURI
	 * @param parents
	 */
	@Deprecated
	public SSSNode(String pLabel, String pURI,ArrayList<SSSNode> parents) {
		super();
		PLabel = pLabel.trim();
		PURI = pURI.trim();
		knownParentClasses = parents;
		
		//add parent classes to 
		addParentClassesToSets(knownParentClasses,"Internal");
		
		Log.warning("******************New Node!! "+pURI+"("+PLabel+") Created TotalNodes = "+knownNodes.size());
		
		knownNodes.add(this);
		
	}
	
	/** does not iterate, direct parents only**/
	public boolean isOrHasDirectParentClass(String classURI){
		
		if (PURI.equalsIgnoreCase(classURI)){
			return true;
		} 
		
		Iterator<SSSNode>  parents = knownParentClasses.iterator();
		while (parents.hasNext()) {
			
			SSSNode sssNode = (SSSNode) parents.next();
			if (sssNode.PURI.equalsIgnoreCase(classURI)){
				return true;
			} 
			
		}
		return false;
	}

	//checks its URI and all equilivents
	public boolean hasURI(String URI){
		
		//if URI does no
		//add the defaultNS if needed:
		//Log.info("testing for="+URI);
		
		
		if (PURI.equalsIgnoreCase(URI)){
			return true;
		}
		
		//check eqilivents
		Iterator<String> equits = altURIs.iterator();
		//Log.info("equNodes length="+altURIs.size());
		
		while (equits.hasNext()) {
			
			//should be a check here to avoid a A>>B>>C>>A cycle
			//currently this crashs
			//because the equalivant has THIS class as an equilivent
			//SSSNode sssNode = (SSSNode) equits.next();
			
			String equiv_uri = equits.next();
			
			if (equiv_uri.equalsIgnoreCase(URI)){
				return true;				
			}
			//WE used to use regression here to call this function on all its equalevent nodes.
			//This was stupid as it caused the above explain problems/
		   //It was also pointless,as we could have just done the IF statement above
			
			/*
			//skip it if its the current node			
			 if (sssNode==this){
				 continue;
			 }
			
			 if (sssNode.hasURI(URI)){
				 return true;
			 }*/
			
		}
		
		
		return false;
		
		
	}
	
	// if this is a class, or it has a parent with this class
	
	// to detect a parent which this is a class of, we look for a common property which contains this 
	// as a class
	// This is then cached in its knownParentClasses list.
	
	public boolean isOrHasParentClass(String classURI){
		
		classURI=classURI.trim();
		
		// Log.info("testing for parent with:" + classURI);
		
		if (hasURI(classURI)){
			return true;
		} 
	
		
		//The following refreshes the cache	, we shouldnt do this each time, but rather only when the database changes	
		if (NeedsUpdate){
			refreshParentCache();
			//also child?
		}
		
		Iterator<SSSNode> kpC = knownParentClasses.iterator();
		while (kpC.hasNext()) {
			
			SSSNode sssNode = (SSSNode) kpC.next();
			
			if (sssNode.isOrHasParentClass(classURI)){

				return true;
			}
		}
		
		return false;
		
		// check the cache (future support)
	/*	Iterator<SSSNode>  parents = knownParentClasses.iterator();
		while (parents.hasNext()) {
			
			SSSNode sssNode = (SSSNode) parents.next();
			
			if (sssNode.isOrHasParentClass(classURI)){
				
				return true;
			} 
			
		}
		return false;*/
	}
	
	/*----------------------------------------
	private void refreshParentCache_old() {
		//sets that contain this node
		HashSet<SSSNodesWithCommonProperty> parents = SSSNodesWithCommonProperty.getCommonPropertySetsContaining(PURI);
						
		//loop over parent sets				
		Iterator<SSSNodesWithCommonProperty>  parentsIt = parents.iterator();
		
		while (parentsIt.hasNext()) {
			
			SSSNodesWithCommonProperty sssNodesWithCommonProperty = (SSSNodesWithCommonProperty) parentsIt
					.next();
			
			//get the parents class
			SSSNode val = sssNodesWithCommonProperty.commonValue;
			SSSNode pred = sssNodesWithCommonProperty.commonPrec; //should be "subclassof" only
			if (pred.equals(SSSNode.SubClassOf)){			
			
			 Log.info("adding parent:" + pred.PLabel+" "+val.PLabel);			
			//add to parent cache
			 this.addParentClasses(val);
			}
		}
		
		NeedsUpdate=false;
	}
	*/
	
	private void refreshParentCache() {
		//sets that contain this node
		HashSet<SSSNodesWithCommonProperty> parents = SSSNodesWithCommonProperty.getCommonPropertySetsContaining(PURI);
		
		//if no parent sets we exit
		if (parents.size()==0){
			return;
		}
		
		//loop over parent sets				
		final Iterator<SSSNodesWithCommonProperty>  parentsIt = parents.iterator();		
		final SSSNode thisNode =this;
		
		
		
		SuperSimpleSemantics.waitForRep.scheduleAfter(new MyRepeatingCommand() {

			@Override
			public boolean execute() {
				
				SSSNodesWithCommonProperty sssNodesWithCommonProperty = (SSSNodesWithCommonProperty) parentsIt
						.next();
				
				//get the parents class
				SSSNode val = sssNodesWithCommonProperty.getCommonValue();
				SSSNode pred = sssNodesWithCommonProperty.getCommonPrec(); //should be "subclassof" only
				if (pred.equals(SSSNode.SubClassOf)){			
				
				 Log.info("adding parent:" + pred.PLabel+" "+val.PLabel);			
				//add to parent cache
				 thisNode.addParentClasses(val,"",false);
				}
				
				
				return parentsIt.hasNext();
			}
			
		});
		
	}
	
	public void updateCachesOnNextUse(){
		//NeedsUpdate=true;

		NeedsUpdate=false;
	}
	
	//we need to refresh the child cache too
	private void refreshChildCache() {
		
		SuperSimpleSemantics.info("Refreshing child cache");
		
		SSSNodesWithCommonProperty children = SSSNodesWithCommonProperty.getSetFor(SSSNode.SubClassOf, this);
		
		if (children!=null){
		
		for (SSSNode sssNode : children) {
			
			this.addChildClasses(sssNode);
		}
		
		}
	}
	
	public boolean hasLabel(String label){
		
		//detects special labels like StartsWith=
		//if (label.startsWith("StartsWith=")){
			//
		//}
		
		// Number=<5
		// "<5" is the special label searched for
		// every node under 5 returns true
		
		// Note; Things searching for nodes with labels less then 5 will have
		// to be adapted to support multiple nodes resulting from this search.
		
		
		if (PLabel.equalsIgnoreCase(label)){
			return true;
		} 
		
		Iterator<String>  names = altLabs.iterator();
		while (names.hasNext()) {
			String name = (String) names.next();
			
			if (name.equalsIgnoreCase(label)){
				return true;
			}
			
		}
		return false;
	}
	
	public HashSet<SSSNode> getAllClassesThisBelongsToo(){
		
		//start with direct parents
		HashSet<SSSNode> tempNodes = new HashSet<SSSNode>();
		
		tempNodes.addAll(knownParentClasses);
		
		//loop over parents adding their parents
		Iterator<SSSNode>  parents = knownParentClasses.iterator();
		while (parents.hasNext()) {
			
			SSSNode sssNode = (SSSNode) parents.next();
			tempNodes.addAll(sssNode.getAllClassesThisBelongsToo());
			
		}
		
		return tempNodes;
		
		
	}
	
	/** returns propertys of the form SubClassOf [x] where x is parent classes of
	 * this. **/
public HashSet<SSSProperty> getAllClassesThisBelongsToAsPropertys(){
		
	HashSet<SSSProperty> allProps = new HashSet<SSSProperty>();
	
	//start with getting all parents as nodes
	HashSet<SSSNode> allParentNodes = getAllClassesThisBelongsToo();
	
	for (SSSNode sssNode : allParentNodes) {
		
		SSSProperty newprop = new SSSProperty(SubClassOf,sssNode);
		
		allProps.add(newprop);
		
		
	}
	
		return allProps;
		
		
	}
	public ArrayList<SSSNode> getKnownDirectSubclassesOfThis() {
		
		//Sanity check: ensure it isnt on its own subclass list
		//class-ception is not a good idea
		knownSubclassesOfThis.remove(this);
		
		return knownSubclassesOfThis;
	}
	public ArrayList<SSSNode> getKnownDirectParentClasses() {
		//Sanity check: ensure it isnt on its own subclass list
				//class-ception is not a good idea
		knownParentClasses.remove(this);
				
		return knownParentClasses;
	}


	public void addParentClasses(SSSNode newParentClass,String accordingToSource) {
		addParentClasses(newParentClass,accordingToSource,true);
	}
	
	
	public void addParentClasses(SSSNode newParentClass,String accordingToSource,Boolean addToSets) {
		
		//make sure its not on the list already, and its not itself
		//(cant have a parent of itself, as we get into a infinite loop of doom!)
		//
		if ((!knownParentClasses.contains(newParentClass))&&(!newParentClass.equals(this))){
			
		knownParentClasses.add(newParentClass);
		
		newParentClass.addChildClasses(this);
		
		}
		//just added, as this was forgotten
		if (addToSets){
			addParentClassesToSets(knownParentClasses,accordingToSource);
		}
	}
	public void addChildClasses(SSSNode newChildClass) {

		//make sure its not on the list already, and its not itself
		//(cant have a parent of itself, as we get into a infinite loop of doom!)
		//
		if ((!knownSubclassesOfThis.contains(newChildClass))&&(!newChildClass.equals(this))){
			knownSubclassesOfThis.add(newChildClass);
		}
	}
	static public SSSNode getNodeByUri(String uri){
		
		//quick check if its a subclass or label
		//WE CHECK FOR BOTH simple and expanded forms
		//we could add these to the knownNodes in the setup, rather then the check here.
		if (uri.equalsIgnoreCase("rdfs:subClassOf")||(uri.equalsIgnoreCase("http://www.w3.org/2000/01/rdf-schema#subClassOf"))){
			return SSSNode.SubClassOf;
		}
		if (uri.equalsIgnoreCase("rdfs:label")||uri.equalsIgnoreCase("http://www.w3.org/2000/01/rdf-schema#label")){
			return SSSNode.Label;
		}
		if (uri.equalsIgnoreCase("owl:equivalentClass")||uri.equalsIgnoreCase("http://www.w3.org/2002/07/owl#equivalentclass")){
			Log.warning("____________ SSSNode.Equivlient:"+uri);
			return SSSNode.Equivlient;
		}
		
		
		//expand if needed
		
	//expand prefix if needed
	if (uri.contains(":")){
		Log.info("potential prefix detected");
		uri = RawQueryUtilities.getURI(uri);
		
	}
	
		Iterator<SSSNode> nodes = knownNodes.iterator();
		
		while (nodes.hasNext()) {
			
			SSSNode sssNode = (SSSNode) nodes.next();
			
			if (sssNode.hasURI(uri)){
			//if (sssNode.PURI.equalsIgnoreCase(uri)){
				return sssNode;
			}
			
		}
		Log.info("no existing node found for: "+uri);
		
		return NOTFOUND;
	}
	
	/** Search over the known SSSNodes and return the first one that has the specified label **/
	static public SSSNode getNodeByLabel(String label){
		

		Log.info("getting node by label:"+label);
		
		Iterator<SSSNode> nodes = knownNodes.iterator();
		
		while (nodes.hasNext()) {
			
			SSSNode sssNode = (SSSNode) nodes.next();
			
			if (sssNode.hasLabel(label)){
				return sssNode;
			}
			
			
			
			
			
		}
		return NOTFOUND;
	}
	
	
	/** returns a copy of all the known nodes **/
	static public ArrayList<SSSNode> getAllKnownNodes(){
		
		return (ArrayList<SSSNode>) knownNodes.clone();
	}
	
	
	public void addLabel(String newLabel) {
		
		Log.info("adding label: "+newLabel+" to "+PURI);

		//remove any quotes
		newLabel = newLabel.replaceAll("\"", "");

		//if there's no primary label, we use it
		if (!hasLabel())
		{
			Log.info("setting PLabel to: "+newLabel
					+ " PLabel was "+PLabel);
			PLabel=newLabel;
			return;
		}
		altLabs.add(newLabel);
		
		
		
	}
	
	public String getDirectParentsAsString() {
		
		
		Iterator<SSSNode> parit = knownParentClasses.iterator();

		String str=":";//+knownParentClasses.size()+":";
		
		
		while (parit.hasNext()) {
			
					
			SSSNode sssNode = (SSSNode) parit.next();
			//ignore its own entry
			if (sssNode==this){
				continue;
			}
			
			str=str+","+sssNode.PURI+"("+sssNode.PLabel+")";
			
		}
		
		return str;
	}

	public static void clearAllKnowenNodes() {
		
		knownNodes.clear();
		
	}
	
	@Override
	public String toString(){
		
		if (extendedDebug){
			return PURI+" {"+PLabel+"} \n"; //note I have changed this to {} style brackets so if someone uses a node directly in a query, rather then a nodes URI (this.getPUri) it will be clearly cause an error
		}
		
		return PLabel;
		
	}

	public String getDirectChildrenAsString() {
		
		//shouldn't do this unless needed? how to optimise?
		//refreshChildCache();
		
		return knownSubclassesOfThis.toString();
		
	}

	/** Refreshes the nodes local caches of parent and children 
	 * this can be slow if done on mass **/
	public static void refreshAllCaches() {
		
		SuperSimpleSemantics.setCurrentLoadProcess("refreshing node cache");
		
		Log.warning("refreshing caches");
		// loop over and refresh all nodes
		for (final SSSNode current_node : knownNodes) {
			//we give the interface time to refresh between each node
			SuperSimpleSemantics.waitFor.scheduleAfter(new Runnable() {				
				@Override
				public void run() {
					current_node.refreshChildCache();
					current_node.refreshParentCache();
				}
			});
			
		
			
		}
		
	}

	public boolean isOrHasChildClass(SSSNode classToTest) {
		
		if (classToTest==this){
			return true;
		}
		
		//check childs
		for (SSSNode child : knownSubclassesOfThis) {
			
			if (child.isOrHasChildClass(classToTest)){
				
				return true;
			}
			
		}
		
		return false;
	}
}
